/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
/*
 * Created on 04.04.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package robocup.component.worldmodel;

import robocup.component.WorldModel;
import robocup.component.infotypes.Info;
import robocup.component.infotypes.PlayerSayInfo;
import robocup.component.infotypes.SenseBodyInfo;
import robocup.component.infotypes.VisualInfo;
import robocup.component.speechacts.MessageFactory.MESSAGES;
import robocup.component.util.RingList;

/**
 * This memory stores data over time and is the complement to the 
 * {@link ShortTermMemory}. 
 * 
 */
public class LongTermMemory {

  /**
   * class for storing informations about a {@link PlayerSayInfo}.
   */
	private class HearData {

    //sender
		private int from;

    //arrived when?
		private int cycle;

    //of what type?
		private MESSAGES mType;

		/**
		 * Constructor.
     * 
		 * @param cycle - the when the message arrived
     * @param from -
		 *            heared player
		 * @param type -
		 *            message type
		 */
		public HearData(int cycle, int from, MESSAGES type) {

			this.cycle = cycle;
			this.from = from;
			this.mType = type;
		}

		/**
		 * @return Returns the from.
		 */
		public int getFrom() {

			return this.from;
		}

		/**
		 * @param from
		 *            The from to set.
		 */
		public void setFrom(int from) {

			this.from = from;
		}

		/**
		 * @return Returns the type.
		 */
		public MESSAGES getType() {

			return this.mType;
		}

		/**
		 * @param type
		 *            The type to set.
		 */
		public void setType(MESSAGES type) {

			this.mType = type;
		}

		/**
		 * @return Returns the cycle.
		 */
		public int getCycle() {

			return this.cycle;
		}

		/**
		 * @param cycle
		 *            The cycle to set.
		 */
		public void setCycle(int cycle) {

			this.cycle = cycle;
		}
	}

	// length of RingLists (number of cycles to be stored)
	private int memoryLength = 30;

	// arrival times of VI Infos (Time taken at UDP arrival)
	private RingList<Long> arrivalTimesVI = new RingList<Long>(
			this.memoryLength);

	// arrival times of BS Infos (Time taken at UDP arrival)
	private RingList<Long> arrivalTimesBS = new RingList<Long>(
			this.memoryLength);

	// the length of the last cycles
	private RingList<Long> cycleLength = new RingList<Long>(this.memoryLength);

	// time between vi-arrival and sending of the resulting action (45°)
	private RingList<Long> thinkTimes45 = new RingList<Long>(this.memoryLength);

  //think times when the player has the ball
	private RingList<Long> thinkTimesWithBall = new RingList<Long>(
			this.memoryLength);

  //last heared messages
	private RingList<HearData> hearMessages = new RingList<HearData>(
			this.memoryLength);

  //the world
	private WorldModel world;

	/**
	 * Constructor - a class stroing long-term information within a certain time
	 * using <code>RingList</code>s.
	 * 
	 * @param world -
	 *            the current world-model
	 */
	public LongTermMemory(WorldModel world) {
		this.world = world;
	}

  /**
   * Remember arriving infos.
   * If it was a {@link PlayerSayInfo} store the message data.
   * 
   * @param info - the info
   */
	public void insertInfo(Info info) {

		if (info.getInfoType() == Info.TYPE_VISUAL) {
			insertInfo((VisualInfo) info);
		} else if (info.getInfoType() == Info.TYPE_SENSE) {
			insertInfo((SenseBodyInfo) info);
		} else if (info.getInfoType() == Info.TYPE_AURAL) {
			// insertInfo((AuralInfo)info);
		} else if (info.getInfoType() == Info.TYPE_AURAL_PSI) {
			PlayerSayInfo psi = (PlayerSayInfo) info;
			char c = psi.getMessage().charAt(0);
			MESSAGES m = (MESSAGES.values())[this.world.getMessageFactory().valuesOfChars[c]];
			HearData hearData = new HearData(psi.getCycle(), psi.getNumber(), m);
			this.hearMessages.insert(hearData);
		}
	}

	/**
	 * Called after info arrival in wm.
	 * 
	 * @param _info
	 *            the info that arrived.
	 */
	public void insertInfo(SenseBodyInfo _info) {

		if (this.arrivalTimesBS.length() > 0) {
			this.cycleLength.insert(_info.getArrivalTime()
					- this.arrivalTimesBS.getLast());
		}
		this.arrivalTimesBS.insert(_info.getArrivalTime());
	}

	/**
	 * Called after info arrival in wm.
	 * 
	 * @param _info
	 *            the info that arrived.
	 */
	public void insertInfo(VisualInfo _info) {

		this.arrivalTimesVI.insert(_info.getArrivalTime());
	}

	/**
	 * The last SenseBodyInfo. null if empty
	 * 
	 * @return the last SenseBodyInfo, null if empty
	 */
	public long getLastBSArrivalTime() {

		return this.arrivalTimesBS.getLast();
	}

	/**
	 * The last VisualInfo. null if empty
	 * 
	 * @return the last VisualInfo, null if empty
	 */
	public long getLastVIArrivalTime() {

		return this.arrivalTimesVI.getLast();
	}

  /**
   * The last SenseBody info arrival times in ms.
   * 
   * @return {@link RingList} containing the last SenseBody 
   * info arrival times in ms.
   */
	public RingList<Long> getBSArrivalTimes() {

		return this.arrivalTimesBS;
	}

  /**
   * The last visual info arrival times in ms.
   * 
   * @return {@link RingList} containing the last visual 
   * info arrival times in ms.
   */
	public RingList<Long> getVIArrivalTimes() {

		return this.arrivalTimesVI;
	}

  /**
   * The arrival time for a BSInfo given by index.
   * 
   * @param i - the index (0 = this cycle, 1 = last cyle, etc.)
   * @return the arrival time in ms
   */
	public long getLastBSArrivalTime(int i) {

		return this.arrivalTimesBS.getLast(i);
	}

  /**
   * The arrival time for a VisualInfo given by index.
   * 
   * @param i - the index (0 = this cycle, 1 = last cyle, etc.)
   * @return the arrival time in ms
   */
	public long getLastVIArrivalTime(int i) {

		return this.arrivalTimesVI.getLast(i);
	}

	/**
	 * @return Returns a {@link RingList} containing the cycle Length.
	 */
	public RingList<Long> getCycleLength() {

		return this.cycleLength;
	}

	/**
	 * @return Returns a {@link RingList} containg the thinkTimes45 in ms.
	 */
	public RingList<Long> getThinkTimes45() {

		return this.thinkTimes45;
	}

  /**
   * @return Returns a {@link RingList} containing all thinkTimesWithBall.
   */
  public RingList<Long> getThinkTimesWithBall() {

    return this.thinkTimesWithBall;
  }

  /**
   * The sender of a message.
   * 
   * @param cycleDiff - cycleDiff of the message' arrival (0 = this cycle,
   * 1 = last cycle, etc.)
   * @return the player heared in this cycle
   */
	public int getHearedMessageSender(int cycleDiff) {
		if (cycleDiff > this.hearMessages.length() - 1)
			return -1;
		return this.hearMessages.getLast(cycleDiff).getFrom();
	}

  /**
   * The message type of a given message.
   * 
   * @param cycleDiff - cycleDiff of the message' arrival (0 = this cycle,
   * 1 = last cycle, etc.)
   * @return the type of the message heared in this cycle
   */
	public MESSAGES getHearedMessageType(int cycleDiff) {
		if (cycleDiff > this.hearMessages.length() - 1)
			return null;
		return this.hearMessages.getLast(cycleDiff).getType();
	}

}
